/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using Utils = System.Qizx.Util.Basic.Utils;
namespace System.Qizx.Util
{
	
	
	/// <summary> Pattern similar to fileNAME expansion (not full path, because ignores '/').
	/// SQL-LIKE mapped to this by substituting meta characters.
	/// <p>Case-insensitive by default.
	/// </summary>
	public class LikePattern:StringPattern
	{
		virtual public bool CaseSensitive
		{
			get
			{
				return _caseSensitive;
			}
			
			set
			{
				this._caseSensitive = value;
			}
			
		}
		public LikePattern(string pattern):this(pattern.ToCharArray(), pattern.Length)
		{
		}
		
		public LikePattern(char[] pattern, int length):base(pattern, length)
		{
			
			int pos = 0;
			if (pattern != null)
			{
				for (; pos < pattern.Length; pos++)
					if (pattern[pos] == '*' || pattern[pos] == '?' || pattern[pos] == '[')
						break;
				_fixedPrefix = new char[pos];
				Array.Copy(pattern, 0, _fixedPrefix, 0, pos);
				_fixedPrefixStart = pos;
			}
		}
		
		public override bool matches(string string_Renamed)
		{
			return matches(string_Renamed.ToCharArray());
		}
		
		public override bool matches(char[] string_Renamed)
		{
			return glob(string_Renamed, 0, 0);
		}
		
		public override int match(char[] string_Renamed)
		{
			
			// compare fixed prefix: used to determine if beyond
			int plen = _fixedPrefix.Length;
			int cmp = Utils.prefixCompare(_fixedPrefix, string_Renamed, plen, _caseSensitive, true);
			if (cmp != 0)
				return (cmp < 0)?BEYOND:NOMATCH;
			// normal matching from end of fixed prefix:
			return glob(string_Renamed, plen, _fixedPrefixStart)?MATCH:NOMATCH;
		}
		
		public virtual bool glob(char[] string_Renamed, int ps, int pp)
		{
			int slen = string_Renamed.Length;
			int plen = _pattern.Length;
			
			for (; ; ++pp, ++ps)
			{
				// See if we're at the end of both the pattern and the string.
				// If so, we succeeded. If we're at the end of the pattern
				// but not at the end of the string, we failed.
				if (pp == plen)
					return (ps == slen);
				
				if (ps == slen && _pattern[pp] != '*')
					return false;
				
				// Check for a "*" as the next pattern character. It matches
				// any substring. We handle this by calling ourselves
				// recursively for each postfix of string, until either we
				// match or we reach the end of the string.
				if (_pattern[pp] == '*')
				{
					++pp;
					if (pp == plen)
						return true; // no need to go on
					
					for (; ; ps++)
					{
						if (glob(string_Renamed, ps, pp))
							return true;
						if (ps == slen)
							return false;
					}
				}
				
				// Check for a "?" as the next pattern character. It matches
				// any single character.
				if (_pattern[pp] == '?')
					continue;
				else if (_pattern[pp] == '[')
				{
					++pp;
					bool negated = false;
					if (pp < plen && _pattern[pp] == '^')
					{
						negated = true;
						++pp;
					}
					for (; ; )
					{
						if (_pattern[pp] == ']' || pp >= plen)
						{
							// end of range reached: success if negated, else
							// failure
							if (!negated)
								return false;
							break;
						}
						else if (_pattern[pp] == string_Renamed[ps])
						{
							if (negated)
								return false;
							break;
						}
						if (_pattern[pp + 1] == '-' && pp < plen - 2)
						{
							if (_pattern[pp] <= string_Renamed[ps] && string_Renamed[ps] <= _pattern[pp + 2])
							{
								if (negated)
									return false;
								break;
							}
							pp += 2;
						}
						++pp;
					}
					// in case of match, skip remainder of [char set]
					while (pp < plen && _pattern[pp] != ']')
					{
						++pp;
					}
					continue;
				}
				/*
				* If the next pattern character is '\', just strip it off to do
				* exact matching on the character that follows.
				*/
				if (_pattern[pp] == '\\')
					if (++pp == plen)
						return false;
				
				// There's no special character. Just make sure that the next
				// characters of each string match.
				if (_caseSensitive)
				{
					if (_pattern[pp] != string_Renamed[ps])
						return false;
				}
				else
				{
					if (System.Char.ToUpper(_pattern[pp]) != System.Char.ToUpper(string_Renamed[ps]))
						return false;
				}
			}
		}
		
		public override string ToString()
		{
			return new System.String(_pattern);
		}
	}
}